import { Injectable, NotFoundException, BadRequestException, ForbiddenException } from '@nestjs/common'
import { DatabaseService } from '../../database/database.service'
import { LoggerService } from '../../common/logger/logger.service'
import { UserRole } from '@prisma/client'
import * as fs from 'fs'
import * as path from 'path'

@Injectable()
export class FileService {
  constructor(
    private readonly databaseService: DatabaseService,
    private readonly logger: LoggerService
  ) {}

  // 获取文件列表
  async getFiles(
    params: {
      page?: number
      limit?: number
      type?: string
      userId?: number
      search?: string
    },
    requestUserId: number,
    userRole: UserRole
  ) {
    try {
      const { page = 1, limit = 20, type, userId, search } = params
      const skip = (page - 1) * limit

      const where: any = {}

      // 非管理员只能查看自己的文件
      if (userRole !== UserRole.ADMIN && userRole !== UserRole.SUPER_ADMIN) {
        where.uploadedBy = requestUserId.toString()
      } else if (userId) {
        where.uploadedBy = userId.toString()
      }

      if (type) {
        where.mimeType = { contains: type }
      }

      if (search) {
        where.OR = [
          { originalName: { contains: search, mode: 'insensitive' } },
          { filename: { contains: search, mode: 'insensitive' } }
        ]
      }

      const [files, total] = await Promise.all([
        this.databaseService.file.findMany({
          where,
          skip,
          take: limit,
          orderBy: { createdAt: 'desc' },
          include: {
            uploader: {
              select: { id: true, username: true, email: true }
            }
          }
        }),
        this.databaseService.file.count({ where })
      ])

      return {
        data: files,
        pagination: {
          page,
          limit,
          total,
          pages: Math.ceil(total / limit)
        }
      }
    } catch (error) {
      this.logger.error(`Failed to get files: ${error.message}`, error.stack, 'FileService')
      throw new BadRequestException('获取文件列表失败')
    }
  }

  // 根据ID获取文件
  async getFileById(id: string, requestUserId: number, userRole: UserRole) {
    try {
      const file = await this.databaseService.file.findUnique({
        where: { id },
        include: {
          uploader: {
            select: { id: true, username: true, email: true }
          }
        }
      })

      if (!file) {
        throw new NotFoundException('文件不存在')
      }

      // 权限检查：非管理员只能查看自己的文件
      if (
        userRole !== UserRole.ADMIN &&
        userRole !== UserRole.SUPER_ADMIN &&
        file.uploadedBy !== requestUserId.toString()
      ) {
        throw new ForbiddenException('无权限访问此文件')
      }

      return file
    } catch (error) {
      if (error instanceof NotFoundException || error instanceof ForbiddenException) {
        throw error
      }
      this.logger.error(`Failed to get file: ${error.message}`, error.stack, 'FileService')
      throw new BadRequestException('获取文件详情失败')
    }
  }

  // 删除文件
  async deleteFile(id: string, requestUserId: number, userRole: UserRole) {
    try {
      const file = await this.databaseService.file.findUnique({
        where: { id }
      })

      if (!file) {
        throw new NotFoundException('文件不存在')
      }

      // 权限检查：只有文件上传者或管理员可以删除
      if (
        userRole !== UserRole.ADMIN &&
        userRole !== UserRole.SUPER_ADMIN &&
        file.uploadedBy !== requestUserId.toString()
      ) {
        throw new ForbiddenException('无权限删除此文件')
      }

      // 删除物理文件
      try {
        const filePath = path.join(process.cwd(), 'uploads', file.path)
        if (fs.existsSync(filePath)) {
          fs.unlinkSync(filePath)
        }
      } catch (fsError) {
        this.logger.warn(`Failed to delete physical file: ${fsError.message}`, 'FileService')
      }

      // 删除数据库记录
      await this.databaseService.file.delete({
        where: { id }
      })

      this.logger.log(`File deleted: ${id}`, 'FileService')
      return { message: '文件删除成功' }
    } catch (error) {
      if (error instanceof NotFoundException || error instanceof ForbiddenException) {
        throw error
      }
      this.logger.error(`Failed to delete file: ${error.message}`, error.stack, 'FileService')
      throw new BadRequestException('删除文件失败')
    }
  }

  // 批量删除文件
  async deleteFiles(ids: string[], requestUserId: number, userRole: UserRole) {
    try {
      const files = await this.databaseService.file.findMany({
        where: { id: { in: ids } }
      })

      if (files.length === 0) {
        throw new NotFoundException('未找到要删除的文件')
      }

      // 权限检查
      for (const file of files) {
        if (
          userRole !== UserRole.ADMIN &&
          userRole !== UserRole.SUPER_ADMIN &&
          file.uploadedBy !== requestUserId.toString()
        ) {
          throw new ForbiddenException(`无权限删除文件: ${file.originalName}`)
        }
      }

      // 删除物理文件
      const deletedFiles: string[] = []
      const failedFiles: string[] = []

      for (const file of files) {
        try {
          const filePath = path.join(process.cwd(), 'uploads', file.path)
          if (fs.existsSync(filePath)) {
            fs.unlinkSync(filePath)
          }
          deletedFiles.push(file.originalName)
        } catch (fsError) {
          this.logger.warn(`Failed to delete physical file ${file.originalName}: ${fsError.message}`, 'FileService')
          failedFiles.push(file.originalName)
        }
      }

      // 删除数据库记录
      await this.databaseService.file.deleteMany({
        where: { id: { in: ids } }
      })

      this.logger.log(`Batch delete files: ${ids.join(', ')}`, 'FileService')

      return {
        message: '批量删除完成',
        deleted: deletedFiles,
        failed: failedFiles,
        total: files.length
      }
    } catch (error) {
      if (error instanceof NotFoundException || error instanceof ForbiddenException) {
        throw error
      }
      this.logger.error(`Failed to batch delete files: ${error.message}`, error.stack, 'FileService')
      throw new BadRequestException('批量删除文件失败')
    }
  }

  // 更新文件信息
  async updateFile(
    id: string,
    data: {
      originalName?: string
      description?: string
    },
    requestUserId: number,
    userRole: UserRole
  ) {
    try {
      const file = await this.databaseService.file.findUnique({
        where: { id }
      })

      if (!file) {
        throw new NotFoundException('文件不存在')
      }

      // 权限检查：只有文件上传者或管理员可以更新
      if (
        userRole !== UserRole.ADMIN &&
        userRole !== UserRole.SUPER_ADMIN &&
        file.uploadedBy !== requestUserId.toString()
      ) {
        throw new ForbiddenException('无权限修改此文件')
      }

      const updatedFile = await this.databaseService.file.update({
        where: { id },
        data: {
          ...data
        },
        include: {
          uploader: {
            select: { id: true, username: true, email: true }
          }
        }
      })

      this.logger.log(`File updated: ${id}`, 'FileService')
      return updatedFile
    } catch (error) {
      if (error instanceof NotFoundException || error instanceof ForbiddenException) {
        throw error
      }
      this.logger.error(`Failed to update file: ${error.message}`, error.stack, 'FileService')
      throw new BadRequestException('更新文件信息失败')
    }
  }

  // 获取文件统计信息
  async getFileStats(userId?: number) {
    try {
      const where = userId ? { uploadedBy: userId.toString() } : {}

      const [totalFiles, totalSize, imageCount, documentCount, videoCount, audioCount] = await Promise.all([
        this.databaseService.file.count({ where }),
        this.databaseService.file.aggregate({
          where,
          _sum: { size: true }
        }),
        this.databaseService.file.count({
          where: {
            ...where,
            mimeType: { startsWith: 'image/' }
          }
        }),
        this.databaseService.file.count({
          where: {
            ...where,
            OR: [{ mimeType: { startsWith: 'application/' } }, { mimeType: { startsWith: 'text/' } }]
          }
        }),
        this.databaseService.file.count({
          where: {
            ...where,
            mimeType: { startsWith: 'video/' }
          }
        }),
        this.databaseService.file.count({
          where: {
            ...where,
            mimeType: { startsWith: 'audio/' }
          }
        })
      ])

      return {
        totalFiles,
        totalSize: totalSize._sum.size || 0,
        typeStats: {
          images: imageCount,
          documents: documentCount,
          videos: videoCount,
          audios: audioCount
        }
      }
    } catch (error) {
      this.logger.error(`Failed to get file stats: ${error.message}`, error.stack, 'FileService')
      throw new BadRequestException('获取文件统计失败')
    }
  }

  // 清理未使用的文件
  async cleanupUnusedFiles() {
    try {
      // 获取所有文件记录
      const allFiles = await this.databaseService.file.findMany({
        select: { id: true, path: true, originalName: true }
      })

      const uploadsDir = path.join(process.cwd(), 'uploads')
      const physicalFiles = this.getAllFiles(uploadsDir)

      const dbFilePaths = new Set(allFiles.map(f => path.join(uploadsDir, f.path)))
      const orphanedFiles: string[] = []

      // 找出物理文件中不在数据库中的文件
      for (const physicalFile of physicalFiles) {
        if (!dbFilePaths.has(physicalFile)) {
          orphanedFiles.push(physicalFile)
        }
      }

      // 删除孤立的物理文件
      let deletedCount = 0
      for (const orphanedFile of orphanedFiles) {
        try {
          fs.unlinkSync(orphanedFile)
          deletedCount++
        } catch (error) {
          this.logger.warn(`Failed to delete orphaned file ${orphanedFile}: ${error.message}`, 'FileService')
        }
      }

      this.logger.log(`Cleanup completed: ${deletedCount} orphaned files deleted`, 'FileService')

      return {
        message: '清理完成',
        deletedCount,
        orphanedFiles: orphanedFiles.map(f => path.relative(uploadsDir, f))
      }
    } catch (error) {
      this.logger.error(`Failed to cleanup unused files: ${error.message}`, error.stack, 'FileService')
      throw new BadRequestException('清理未使用文件失败')
    }
  }

  // 递归获取目录下所有文件
  private getAllFiles(dir: string): string[] {
    const files: string[] = []

    if (!fs.existsSync(dir)) {
      return files
    }

    const items = fs.readdirSync(dir)

    for (const item of items) {
      const fullPath = path.join(dir, item)
      const stat = fs.statSync(fullPath)

      if (stat.isDirectory()) {
        files.push(...this.getAllFiles(fullPath))
      } else {
        files.push(fullPath)
      }
    }

    return files
  }

  // 获取文件类型统计
  async getFileTypeStats() {
    try {
      const stats = await this.databaseService.file.groupBy({
        by: ['mimeType'],
        _count: {
          id: true
        },
        _sum: {
          size: true
        },
        orderBy: {
          _count: {
            id: 'desc'
          }
        }
      })

      return stats.map(stat => ({
        mimeType: stat.mimeType,
        count: stat._count.id,
        totalSize: stat._sum.size || 0
      }))
    } catch (error) {
      this.logger.error(`Failed to get file type stats: ${error.message}`, error.stack, 'FileService')
      throw new BadRequestException('获取文件类型统计失败')
    }
  }
}
